package com.qyb.base.java8.chap3;



import com.qyb.base.java8.bean.Apple;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.util.Comparator.comparing;

/**
 * *************************************************************************************************
 * <p/>
 * 实现功能：用不同的排序策略给一个Apple列表排序
 * <p>
 * ------------------------------------------------------------------------------------------------
 * 版本          变更时间             变更人                     变更原因
 * ------------------------------------------------------------------------------------------------
 * 1.0.00      2017/8/8 11:02      秦有宝                     新建
 * <p/>
 * *************************************************************************************************
 */
// 行为参数化、匿名类、Lambda表达式和方法引用。
public class Demo {
    public static void demo1(){
        List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red"), new Apple(120, "green"));
        //1.传递代码
        //void sort(Comparator<? super E> c)   Java中传递策略的方式：它们必须包裹在一个对象里。我们说sort的行为被参数化了：传递给它的排序策略不同，其行为也会不同。
        inventory.sort(new AppleComparator());
        System.out.println(inventory);

        //2.使用匿名类
        inventory.sort(new Comparator<Apple>() {
            public int compare(Apple a1, Apple a2){
                return a1.getWeight().compareTo(a2.getWeight());
            }
        });
        System.out.println(inventory);

        //3.使用Lambda表达式
        inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
        System.out.println(inventory);
        //Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型。那么你的解决方案就可以重写成这样：
        inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
        System.out.println(inventory);
        //Comparator具有一个叫作comparing的静态辅助方法，它可以接受一个Function来提取Comparable键值，并生成一个Comparator对象
        //Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
        inventory.sort(comparing((a) -> a.getWeight()));
        System.out.println(inventory);

        //4.使用方法引用:方法引用就是替代那些转发参数的Lambda表达式的语法糖。
        inventory.sort(comparing(Apple::getWeight));//对库存inventory进行排序，比较苹果的重量。
        System.out.println(inventory);
    }



    public static class AppleComparator implements Comparator<Apple> {
        public int compare(Apple a1, Apple a2){
            return a1.getWeight().compareTo(a2.getWeight());
        }
    }


    //复合Lambda表达式
    public static void demo2(){
        /*可以把多个简单的Lambda复合成复杂的表达式。比如，
        你可以让两个谓词之间做一个or操作，组合成一个更大的谓词。而且，你还可以让一个函数的结
        果成为另一个函数的输入。你可能会想，函数式接口中怎么可能有更多的方法呢？（毕竟，这违
        背了函数式接口的定义啊！）窍门在于，我们即将介绍的方法都是默认方法，也就是说它们不是
        抽象方法。
        */
        List<Apple> inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red"), new Apple(120, "green"));

        //逆序
        inventory.sort(comparing(Apple::getWeight).reversed());//按重量递减排序
        System.out.println(inventory);

        //比较器链
        inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor));
        System.out.println(inventory);

        //谓词复合
        //谓词接口包括三个方法：negate、and和or，让你可以重用已有的Predicate来创建更复
        //杂的谓词。比如，你可以使用negate方法来返回一个Predicate的非，比如苹果不是红的：
        Predicate<Apple> redApple = (Apple s) -> s.getColor()=="red";
        Predicate<Apple> notRedApple = redApple.negate();
        Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
        //表达要么是重（150克以上）的红苹果，要么是绿苹果
        //and和or方法是按照在表达式链中的位置，从左向右确定优先级的。因此，a.or(b).and(c)可以看作(a || b) && c。
        Predicate<Apple> redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));



        //函数复合
        //Function接口为此配了andThen和compose两个默认方法，它们都会返回Function的一个实例。
        //andThen方法会返回一个函数，它先对输入应用一个给定函数，再对输出应用另一个函数。
        //compose方法先把给定的函数用作compose的参数里面给的那个函数，然后再把函数本身用于结果。
        Function<Integer, Integer> f = x -> x + 1;
        Function<Integer, Integer> g = x -> x * 2;

        Function<Integer, Integer> h = f.andThen(g);     //g(f(x))
        int result = h.apply(1);
        System.out.println(result);  //4

        Function<Integer, Integer> h1 = f.compose(g);    //f(g(x))
        result = h1.apply(1);
        System.out.println(result);//3

    }
}
